在前面幾天,我們一直有用到一些工具型別(例如 Pick<User, "id" | "name">
),
今天來做一次系統化整理,並用專案實例示範它們的實戰用途。
我們今天的例子會沿用 User 型別(Day 15 建立的資料模型):
ts
CopyEdit
type User = {
id: string;
name: string;
email: string;
createdAt: Date;
updatedAt: Date;
};
Pick<T, K>
:只取部分屬性ts
CopyEdit
type UserSummary = Pick<User, "id" | "name">;
用途範例:API 列表只需要顯示使用者 ID 與名稱
ts
CopyEdit
function listUsers(): UserSummary[] {
return [
{ id: "u1", name: "Alice" },
{ id: "u2", name: "Bob" },
];
}
心法:當 API 或 UI 只需要部分欄位時,用 Pick 直接從原型別取,避免自己寫新型別。
Omit<T, K>
:排除部分屬性ts
CopyEdit
type CreateUserInput = Omit<User, "id" | "createdAt" | "updatedAt">;
用途範例:新增使用者時,前端不用傳 id 和時間戳
ts
CopyEdit
async function createUser(payload: CreateUserInput) {
return fetch("/users", {
method: "POST",
body: JSON.stringify(payload),
});
}
Partial<T>
:所有屬性改成可選ts
CopyEdit
type UpdateUserInput = Partial<Omit<User, "id">>;
用途範例:更新使用者時,可以只更新部分欄位
ts
CopyEdit
async function updateUser(id: string, payload: UpdateUserInput) {
return fetch(`/users/${id}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
心法:Partial 適合用在「PATCH」或「設定檔更新」這種欄位可選的情境。
Required<T>
:把所有屬性改成必填假設有一個表單型別:
ts
CopyEdit
type UserProfileForm = {
name?: string;
email?: string;
};
用 Required
強制全部必填:
ts
CopyEdit
type UserProfileRequired = Required<UserProfileForm>;
Record<K, T>
:建立固定 key 集合的型別ts
CopyEdit
type Role = "admin" | "editor" | "viewer";
type RolePermissions = Record<Role, string[]>;
const permissions: RolePermissions = {
admin: ["create", "delete", "update"],
editor: ["update"],
viewer: ["read"],
};
實戰用途:權限表、設定檔映射、字典資料結構
ReturnType<T>
:取得函式回傳型別ts
CopyEdit
function getUser() {
return { id: "u1", name: "Alice" };
}
type GetUserReturn = ReturnType<typeof getUser>;
搭配 API 呼叫:
ts
CopyEdit
async function fetchUser(id: string) {
return { id, name: "Alice" };
}
type FetchUserResult = Awaited<ReturnType<typeof fetchUser>>;
心法:讓型別自動跟著函式改動,避免人手動同步。
更新 API 輸入型別:
ts
CopyEdit
type UpdateUserApiInput = Partial<Omit<User, "id" | "createdAt" | "updatedAt">>;
後端 Controller:
ts
CopyEdit
router.patch("/:id", async (req, res) => {
const payload: UpdateUserApiInput = req.body;
// Prisma update 操作...
});
你可以自己做工具型別,例如 Mutable<T>
(移除 readonly):
ts
CopyEdit
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
或 Nullable<T>
(允許 null):
ts
CopyEdit
type Nullable<T> = { [P in keyof T]: T[P] | null };